/*
 * \brief  Tokenizer support
 * \author Norman Feske
 * \date   2006-05-19
 */

/*
 * Copyright (C) 2006-2009 Norman Feske
 * Genode Labs, Feske & Helmuth Systementwicklung GbR
 *
 * This file is part of the Genode OS framework, which is distributed
 * under the terms of the GNU General Public License version 2.
 */

#ifndef _INCLUDE__UTIL__TOKEN_H_
#define _INCLUDE__UTIL__TOKEN_H_

#include <util/string.h>

namespace Genode {

	/*
	 * This class is used to group characters of a string which belong
	 * to one syntactical token types number, identifier, string,
	 * whitespace or another single character.
	 */
	class Token
	{
		public:

			enum Type { SINGLECHAR, NUMBER, IDENT, STRING, WHITESPACE, END };

			/**
			 * Constructor
			 *
			 * \param s  start of string to construct a token from
			 */
			Token(const char *s = 0) : _start(s), _len(0)
			{
				if (_start)
					_len = _calc_len();
			}

			/**
			 * Return type of token
			 */
			Type type()
			{
				if (!_start || !*_start) return END;

				char c = *_start;

				/* determine the type based on the first character */
				if (is_letter(c) || c == '_')         return IDENT;
				if (is_digit(c))                      return NUMBER;
				if (c == '"' && _quoted_string_len()) return STRING;
				if (is_whitespace(c))                 return WHITESPACE;

				return SINGLECHAR;
			}

			/**
			 * Accessors
			 */
			char *start() { return (char *)_start; }
			size_t  len() { return _len; }

			/**
			 * Return token as null-terminated string
			 */
			void string(char *dst, size_t max_len) {
				strncpy(dst, start(), min(len() + 1, max_len)); }

			/**
			 * Return true if token is valid
			 */
			operator bool () const { return _start && _len; }

			/**
			 * Access single characters of token
			 */
			char operator [] (int idx)
			{
				return ((idx >= 0) && ((unsigned)idx < _len)) ? _start[idx] : 0;
			}

			/**
			 * Return next token
			 */
			Token next() { return Token(_start + _len); }

			/**
			 * Return next non-whitespace token
			 */
			Token eat_whitespace() { return (type() == WHITESPACE) ? next() : *this; }

		private:

			const char *_start;
			size_t      _len;

			size_t _quoted_string_len()
			{
				int i = 0;

				for (; !end_of_quote(&_start[i]); i++)

					/* string ends without final quotation mark? too bad! */
					if (!_start[i]) return 0;

				/*
				 * We stopped our search at the character before the
				 * final quotation mark but we return the number of
				 * characters including the quotation marks.
				 */
				return i + 2;
			}

			/**
			 * Return length of token
			 */
			int _calc_len()
			{
				switch (type()) {

				case SINGLECHAR:
					return 1;

				case NUMBER:
					{
						unsigned long l;
						return ascii_to_ulong(_start, &l);
					}

				case IDENT:
					{
						int i = 0;
						while (is_letter(_start[i]) || (_start[i] == '_')
						       || (i && is_digit(_start[i]))) i++;
						return i;
					}

				case STRING:

					return _quoted_string_len();

				case WHITESPACE:
					{
						int i = 0;
						for (; is_whitespace(_start[i]); i++);
						return i;
					}

				case END:
				default:
					return 0;
				}
			}
	};
}

#endif /* _INCLUDE__UTIL__TOKEN_H_ */
